001 /*
002 * Copyright 2005-2006 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.util;
020
021 import java.rmi.RemoteException;
022 import java.rmi.server.UnicastRemoteObject;
023 import java.rmi.NoSuchObjectException;
024 import java.util.EventObject;
025 import java.util.EventListener;
026 import java.util.WeakHashMap;
027
028 /**
029 * A abstract base class that established an event queue and handles event dispatch
030 * operations for listeners declared in a class extending this base class.
031 *
032 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
033 * @version 1.0.2
034 */
035 public abstract class UnicastEventSource extends UnicastRemoteObject implements EventHandler
036 {
037 /**
038 * Internal synchronization lock.
039 */
040 private final Object m_lock = new Object();
041
042 /**
043 * The controller that provides the main event dispath thread.
044 */
045 private final EventQueue m_queue;
046
047 private final WeakHashMap m_listeners = new WeakHashMap();
048
049 private final Logger m_logger;
050
051 /**
052 * Creation of a new <tt>UnicastEventSource</tt>.
053 * @param queue the event queue
054 * @param logger the assigned logging channel
055 * @exception RemoteException if a remote I/O exception occurs
056 */
057 protected UnicastEventSource( EventQueue queue, Logger logger ) throws RemoteException
058 {
059 super();
060 m_queue = queue;
061 m_logger = logger;
062 }
063
064 //--------------------------------------------------------------------------
065 // internal
066 //--------------------------------------------------------------------------
067
068 /**
069 * Return the logging channel assigned to the event source.
070 * @return the logging channel
071 */
072 protected Logger getLocalLogger()
073 {
074 return m_logger;
075 }
076
077 /**
078 * Return the event queue.
079 * @return the queue
080 */
081 protected EventQueue getEventQueue()
082 {
083 return m_queue;
084 }
085
086 /**
087 * Abstract operation to be implemented by classes extending this base class.
088 * An implementation is reposible for the posting of the event to associated
089 * listeners. Event posting will be executed under a separate thread to the
090 * thread that initiated the event post.
091 *
092 * @param event the event to process
093 */
094 public abstract void processEvent( EventObject event );
095
096 /**
097 * Add a listener to the set of listeners handled by this producer.
098 * @param listener the event listener
099 */
100 protected void addListener( EventListener listener )
101 {
102 if( null == listener )
103 {
104 throw new NullPointerException( "listener" );
105 }
106 synchronized( m_lock )
107 {
108 m_listeners.put( listener, null );
109 }
110 }
111
112 /**
113 * Remove a listener to the set of listeners handled by this producer.
114 * @param listener the event listener
115 */
116 protected void removeListener( EventListener listener )
117 {
118 if( null == listener )
119 {
120 throw new NullPointerException( "listener" );
121 }
122 synchronized( m_lock )
123 {
124 m_listeners.remove( listener );
125 }
126 }
127
128
129 /**
130 * Return the array of registered event listeners.
131 *
132 * @return the event listeners
133 */
134 public EventListener[] getEventListeners()
135 {
136 synchronized( m_lock )
137 {
138 return (EventListener[]) m_listeners.keySet().toArray( new EventListener[0] );
139 }
140 }
141
142 /**
143 * Enqueue an event for delivery to registered
144 * listeners unless there are no registered
145 * listeners.
146 * @param event the event to enqueue
147 */
148 protected void enqueueEvent( EventObject event )
149 {
150 if( m_listeners.size() > 0 )
151 {
152 m_queue.enqueueEvent( event );
153 }
154 }
155
156 /**
157 * Return the internal synchronization lock.
158 * @return the lock object
159 */
160 protected Object getLock()
161 {
162 return m_lock;
163 }
164
165 /**
166 * Terminate the event source.
167 */
168 public void terminate()
169 {
170 synchronized( m_lock )
171 {
172 EventListener[] listeners = getEventListeners();
173 for( int i=0; i < listeners.length; i++ )
174 {
175 EventListener listener = listeners[i];
176 removeListener( listener );
177 }
178 }
179
180 Thread thread = new Terminator( this, m_logger );
181 thread.start();
182 }
183
184 /**
185 * Internal class that handles instance retraction for the RMI runtime.
186 */
187 private class Terminator extends Thread
188 {
189 private final UnicastEventSource m_source;
190 private final Logger m_logger;
191
192 /**
193 * Internal class terminator that handles unexport of an event source
194 * under a separate thread.
195 * @param source the event source instance
196 * @param logger the event source logger
197 */
198 Terminator( UnicastEventSource source, Logger logger )
199 {
200 m_source = source;
201 m_logger = logger;
202 }
203
204 /**
205 * Terminator execution.
206 */
207 public void run()
208 {
209 try
210 {
211 UnicastRemoteObject.unexportObject( m_source, true );
212 m_logger.debug( "terminated" );
213 }
214 catch( NoSuchObjectException e )
215 {
216 boolean ignoreThis = true; // objct has not been exported
217 }
218 catch( RemoteException e )
219 {
220 final String error =
221 "Unexpected remote exception while retracting component handler remote reference.";
222 m_logger.warn( error, e );
223 }
224 }
225 }
226 }